<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title></title>

    <link rel="stylesheet" href="snapshot.css">


    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.11.0/sql-wasm.js"></script>
    <!--axios-->
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <script>
      document.addEventListener('DOMContentLoaded', function () {
        new Vue({
          el: '#app',
          data: {
            db: undefined,
            accounts: {},
            tasks: [],
            task_status: {
                running: "运行中",
                pending: "等待运行",
                running_pending: "运行中,等待运行",
                done: "已完成",
                terminated: "中止执行",
                fail: "运行失败",
            },
            running_users: [],
            task_names: [],
            task_boards: [],
            task_filter: {},
            data_list_raw: '{}',
            data_list: [],
            user_task_summaries: {},
            p_total: 0,
            prev_p_total: 0,
            coin_total: 0,
            prev_coin_total: 0,
            sort: '',
            show_panel: 'snapshot',
            keyword: '',
            data_loaded: false,

          },
          mounted() {
            this.load_accounts()
            this.load_task_summary()
            this.init_db().then(() => {
              this.load_snapshots()
            })
          },
          methods: {
            switch_panel(panel){
              this.show_panel = panel
              if (panel == 'snapshot') {
                this.load_accounts()
                this.load_task_summary()
                this.init_db().then(() => {
                  this.load_snapshots()
                })
              }
              if (panel == 'account') {
                this.load_accounts()
              }
              if (panel == 'task') {
                  this.task_filter = {
                    user_id: 0,
                    priority: 0,
                    execute_date: '',
                    task_name: '',
                    status: 'running_pending',
                    retry_only: false
                  }
                  this.load_tasks()
              }
              if (panel == 'taskboard') {
                  this.taskboard_filter = {
                    user_id: 0,
                    date: (new Date()).toISOString().split('T')[0],
                    item_name: '',
                    p_only: false
                  }
                  this.load_task_boards()
              }
            },
            async init_db() {
              try {
                // 初始化 SQL.js
                const SQL = await initSqlJs({
                  locateFile: file => 'https://cdnjs.cloudflare.com/ajax/libs/sql.js/1.11.0/sql-wasm.wasm'
                });

                // 从 URL 或本地路径加载 SQLite 数据库文件
                const response = await fetch('db.sqlite');
                const buffer = await response.arrayBuffer();

                // 创建 SQL.js 数据库实例
                this.db = new SQL.Database(new Uint8Array(buffer));
              } catch (error) {
                console.error("Failed to initialize the database:", error);
              }
            },
            load_accounts(){
              axios.get('api/accounts').then(res => {
                this.accounts = res.data.list;
              })
            },
            load_task_summary(){
              axios.get('api/user_task_summaries').then(res => {
                this.user_task_summaries = res.data.result;
              })
            },
            get_date_list(){
                const days = []
                const today = new Date();
                for(let i=0; i<7; i++) {
                    const date = new Date()
                    date.setDate(today.getDate() - i)
                    days.push(date.toISOString().split('T')[0])
                }
                return days
            },
            load_snapshots(){
              const today = new Date();
              const todayDateString = today.toISOString().split('T')[0];
              const yesterday = new Date();
              yesterday.setDate(today.getDate() - 1);
              const yesterdayDateString = yesterday.toISOString().split('T')[0];
              const result = this.db.exec(
                      `SELECT coin,
                              p,
                              energy,
                              skill_sum,
                              skills,
                              backpack,
                              shot_date,
                              shot_time,
                              s.user_id
                       FROM snapshot s
                                INNER JOIN (SELECT user_id, MAX(shot_date) AS max_date
                                            FROM snapshot
                                            WHERE shot_date <= '${todayDateString}'
                                            GROUP BY user_id) latest
                                           ON s.user_id = latest.user_id
                                               AND s.shot_date = latest.max_date`
              );
              const prev_result = this.db.exec(
                      `SELECT coin,
                              p,
                              energy,
                              skill_sum,
                              skills,
                              backpack,
                              shot_date,
                              shot_time,
                              s.user_id
                       FROM snapshot s
                                INNER JOIN (SELECT user_id, MAX(shot_date) AS max_date
                                            FROM snapshot
                                            WHERE shot_date <= '${yesterdayDateString}'
                                            GROUP BY user_id) latest
                                           ON s.user_id = latest.user_id
                                               AND s.shot_date = latest.max_date`
              );


              // 将查询结果转换为对象数组并存储在 Vue 数据中
              var prev_list = {}
              if (prev_result.length > 0) {
                prev_result[0].values.forEach(row => {
                  const user_id = row[8]
                  prev_list[user_id] = this.extract_snapshot_row(row)
                })
              }
              if (result.length > 0) {
                let list = {}
                result[0].values.forEach(row => {
                  const user_id = row[8]
                  var data = this.extract_snapshot_row(row)
                  data.prev = prev_list[user_id]
                  list[user_id] = data
                })
                this.data_list_raw = JSON.stringify(list);
              }
              this.apply_filter()
            },
            load_tasks(){
                axios.get('api/tasks').then(res => {
                  this.running_users = []
                  this.tasks = []
                  this.task_names = []
                  this.running_users = res.data.running_users
                  this.task_names = res.data.task_names
                  res.data.list.forEach(row => {
                    if (this.task_filter.user_id > 0 && row.user_id != this.task_filter.user_id) {
                      return
                    }
                    if (this.task_filter.priority > 0 && row.priority != this.task_filter.priority) {
                      return
                    }
                    if (this.task_filter.task_name != '' && row.task_name != this.task_filter.task_name) {
                      return
                    }
                    if (this.task_filter.execute_date !=''  && row.execute_date != this.task_filter.execute_date) {
                      return
                    }
                    if (this.task_filter.status != '') {
                      status = this.task_filter.status.split('_')
                      if(!status.includes(row.status)) {
                        return
                      }
                    }
                    if (this.task_filter.retry_only && row.retry == 0) {
                      return
                    }
                    this.tasks.push(row)
                  })
                })
            },
            load_task_boards() {
              this.task_boards = {}
              axios.get('api/task_board_summary?date=' + this.taskboard_filter.date).then(res => {
                this.task_boards = res.data.result
              })
            },
            delete_task(task_id){
              axios.delete('api/task/' + task_id).then(res => {
                this.load_tasks()
              })
            },
            run_next(task_id){
              axios.post('api/task/' + task_id + '/run_next').then(res => {
                this.load_tasks()
              })
            },
            extract_snapshot_row(row) {
              return {
                coin: row[0],
                p: row[1],
                energy: row[2],
                skill_sum: row[3],
                skills: JSON.parse(row[4]),
                backpack: JSON.parse(row[5]),
                last_update: row[7],
                user_id: row[8]
              };
            },
            apply_filter() {
              let sorted_list = Object.entries(JSON.parse(this.data_list_raw))
              sorted_list.forEach(([key, data]) => {
                if (data.energy) {
                  var last_update = new Date(data.last_update)
                  var current = new Date()
                  var diff_minutes = Math.floor((current - last_update) / 1000 / 60)
                  data.new_p = data.p - (data.prev || {}).p || 0
                  data.energy = Math.min(1000, (data.energy + diff_minutes * 0.75).toFixed(1))
                  data.skill_sum = data.skill_sum || 0
                  data.skills = Object.fromEntries(Object.entries(data.skills || {}).sort(([, value1], [, value2]) => value2 - value1))
                }
              })
              if (this.sort == 'p') {
                sorted_list = sorted_list.sort(([, a], [, b]) => b.p - a.p)
              } else if (this.sort == 'p_new') {
                sorted_list = sorted_list.sort(([, a], [, b]) => b.new_p - a.new_p)
              } else if (this.sort == 'energy') {
                sorted_list = sorted_list.sort(([, a], [, b]) => b.energy - a.energy)
              } else if (this.sort == 'coin') {
                sorted_list = sorted_list.sort(([, a], [, b]) => b.coin - a.coin)
              } else if (this.sort == 'skill') {
                sorted_list = sorted_list.sort(([, a], [, b]) => b.skill_sum - a.skill_sum)
              } else if (this.sort == 'active') {
                sorted_list = sorted_list.sort(([, a], [, b]) => {
                  return new Date(a.last_update.replace(' ', 'T')) - new Date(b.last_update.replace(' ', 'T'));
                })
              }
              this.data_list = sorted_list
              this.p_total = 0
              this.prev_p_total = 0
              this.coin_total = 0
              this.prev_coin_total = 0
              this.data_list.forEach(([key, data]) => {
                if (key > 0) {
                  let account = this.accounts[key]
                  if (account && (account.stat ?? true)) {
                    let prev = data.prev || {}
                    this.p_total += data.p || 0
                    this.prev_p_total += prev.p || 0
                    this.coin_total += data.coin
                    this.prev_coin_total += prev.coin || 0
                  }
                }
                if (this.keyword != '') {
                  let filtered_backpack = {}
                  let backpack = data.backpack || {}
                  Object.entries(backpack).forEach(([idx, item], index) => {
                    if (item.name.toLowerCase().includes(this.keyword.toLowerCase())) {
                      filtered_backpack[idx] = item
                    }
                  })
                  data.backpack = filtered_backpack
                }
              });

              Object.entries(this.accounts).forEach(([user_id, account], index) => {
                let user_exist = false
                this.data_list.forEach(([key, data]) => {
                  if (user_id == key) {
                    user_exist = true
                    return
                  }
                })
                if (!user_exist) {
                  this.data_list.push([user_id, {}])
                }
              })
            },
            apply_sort(sort) {
              this.sort = sort
              this.apply_filter()
            },
            apply_search() {
              this.apply_filter()
            },
            time_diff(time_str) {
              // 将时间字符串转换为 Date 对象
              const given_time = new Date(time_str);
              // 获取当前时间
              const current_time = new Date();

              // 计算时间差并转换为秒
              return (current_time - given_time) / 1000;
            },
            format_number(number) {
              if (!number) {
                return '-'
              }
              return number.toLocaleString()
            },
            get_cell_class(index, backpack) {
              let classes = []
              if (index <= 9) {
                classes.push('backpack_head')
              }
              return classes.join(' ')
            },
            get_backpack_qty(index, backpack) {
              backpack = backpack || {}
              if (backpack[index]) {
                return backpack[index].qty
              } else {
                return 0
              }
            },
            get_backpack_name(index, backpack) {
              backpack = backpack || {}
              if (backpack[index]) {
                return backpack[index].name
              } else {
                return ''
              }
            },
            get_backpack_count(user_id) {
              let account = this.accounts[user_id]
              if (!account) {
                return 30
              }
              return account['is_vip'] ? 36 : 30
            },
            is_vip(user_id) {
              let account = this.accounts[user_id]
              if (account) {
                return account['is_vip']
              } else {
                return false
              }
            },
            has_account(user_id) {
              let account = this.accounts[user_id]
              if (account) {
                return true
              } else {
                return false
              }
            }
          }
        });
      })
    </script>
</head>
<body>
<div class="container" id="app">
    <div class="header-row">
        <div class="flex-row pb-10">
            <div class="flex-row pl-20 filter tabbar">
                <button @click="switch_panel('snapshot')" :class="show_panel=='snapshot'?'btn_active':''">账号数据</button>
                <button class="ml-10" @click="switch_panel('account')" :class="show_panel=='account'?'btn_active':''">
                    账号配置
                </button>
                <button class="ml-10" @click="switch_panel('task')" :class="show_panel=='task'?'btn_active':''">任务队列
                </button>
                <button class="ml-10" @click="switch_panel('taskboard')" :class="show_panel=='taskboard'?'btn_active':''">TASKBOARD
                </button>
            </div>
        </div>
    </div>
    <div v-if="show_panel=='account'">
        <table>
            <thead>
            <td>ID</td>
            <td>NO.</td>
            <td>NAME</td>
            <td>邮箱</td>
            <td>推特</td>
            <td>DC</td>
            <td>市场费率</td>
            <td>VIP</td>
            <td>VIP到期时间</td>
            <td>配置</td>
            <td>备注</td>
            </thead>
            <tr v-for="(account, index) in accounts" :key="index">
                <td>{{index}}</td>
                <td>{{account.user_no}}</td>
                <td>
                    <a :href="`https://pixels.tips/players/${account.user_name}`" target="_blank">
                    {{account.user_name}}
                    </a>
                </td>
                <td>{{account.email}}</td>
                <td>{{account.twitter}}</td>
                <td>{{account.discord}}</td>
                <td>{{account.fee_rate}}</td>
                <td>
                    <span v-if="account.is_vip" style="color:green">是</span>
                    <span v-else style="color:red">否</span>
                </td>
                <td>
                    <span class="pr-20">{{account.vip_expire_date}}</span>
                </td>
                <td>{{account.config}}</td>
                <td>{{account.remark}}</td>
            </tr>
        </table>
    </div>
    <div v-if="show_panel=='task'">
        <div class="flex-row pb-10 filter" style="display:inline-block; width: 80%">
            <div style="padding-top: 10px; font-weight:bold;">用户：</div>
            <div v-for="(account, index) in accounts"  style="display:inline-block; width: auto" >
                <button class="ml-10"  @click="task_filter.user_id=index;load_tasks()"
                    v-if="running_users.includes(parseInt(index))" style="background-color:lightgreen"
                    :class="task_filter.user_id==index?'btn_active':''">{{index}}</button>
                <button class="ml-10"  @click="task_filter.user_id=index;load_tasks()"
                    v-else :class="task_filter.user_id==index?'btn_active':''">{{index}}</button>
            </div>
        </div>
        <div class="flex-row pb-10 filter" style="display:inline-block; width: 80%">
            <div style="padding-top: 10px; font-weight:bold;">状态：</div>
            <div v-for="(desc, status) in task_status" style="display:inline-block; width: auto">
                <button class="ml-10 btn_active"  @click="task_filter.status='running_pending';load_tasks()"
                    v-if="task_filter.status==status">{{desc}}</button>
                <button class="ml-10"  @click="task_filter.status=status;load_tasks()"
                    v-else>{{desc}}</button>
            </div>
        </div>
        <div class="flex-row pb-10 filter" style="display:inline-block; width: 80%">
            <div style="padding-top: 10px; font-weight:bold;">任务：</div>
            <div v-for="task_name in task_names" style="display:inline-block; width: auto">
                <button class="ml-10 btn_active"  @click="task_filter.task_name='';load_tasks()"
                    v-if="task_filter.task_name==task_name">{{task_name}}</button>
                <button class="ml-10"  @click="task_filter.task_name=task_name;load_tasks()"
                    v-else>{{task_name}}</button>
            </div>
        </div>
        <div class="flex-row pb-10 filter">
          <div style="padding-top: 10px; font-weight:bold;">筛选：</div>
          <button v-if="task_filter.priority > 0" @click="task_filter.priority=0;load_tasks()" class="btn_active ml-10">priority={{task_filter.priority}}</button>
          <button v-if="task_filter.execute_date != ''" @click="task_filter.execute_date='';load_tasks()" class="btn_active ml-10">date={{task_filter.execute_date}}</button>
          <button v-if="task_filter.retry_only" @click="task_filter.retry_only=false;load_tasks()" class="btn_active ml-10">RETRY</button>
          <div class="ml-10" style="font-size:14px; padding-top:10px">共 {{tasks.length}} 条记录</div>
          <button class="ml-10 btn_active" style="font-size:14px;" @click="load_tasks()">刷新</button>
        </div>
        <table style="border:1px; width: 90%">
            <thead>
            <td align="center" width="50px">用户</td>
            <td align="center" width="50px">优先级</td>
            <td align="center" width="100px">日期</td>
            <td align="center" width="200px">任务</td>
            <td align="center" width="200px">创建时间</td>
            <td align="center" width="200px">执行时间</td>
            <td align="center" width="100px">任务状态</td>
            <td align="center" width="200px">开始时间</td>
            <td align="center" width="200px">结束时间</td>
            <td align="center" width="100px">操作</td>
            </thead>
            <tr v-for="(task, index) in tasks" :key="index">
                <td align="center">
                  <span class="clickable" @click="task_filter.user_id=task.user_id;load_tasks()">
                      <span v-if="is_vip(task.user_id)" style="color:orange; size: 12px">{{task.user_id}}</span>
                      <span v-else>{{task.user_id}}</span>
                  </span>
                </td>
                <td align="center"><span class="clickable" @click="task_filter.priority=task.priority;load_tasks()">p{{task.priority}}</span></td>
                <td align="center"><span class="clickable" @click="task_filter.execute_date=task.execute_date;load_tasks()">{{task.execute_date}}</span></td>
                <td class="pl-5">
                  <span v-if="task.retry>0" style="background-color:red; font-size:12px; padding:1px 2px; border-radius:5px"
                   @click="task_filter.retry_only=true;load_tasks()">RETRY-{{task.retry}}</span>
                  <span class="clickable" :title="task.command" @click="task_filter.task_name=task.task_name;load_tasks()">{{task.task_name}}</span>
                </td>
                <td align="center">{{task.create_time}}</td>
                <td align="center">{{task.execute_time}}</td>
                <td align="center" class="clickable" @click="task_filter.status=task.status;load_tasks()">
                    <span v-if="task.status=='pending'" style="color:gray">等待执行</span>
                    <span v-if="task.status=='running'" style="background-color:lightgreen">执行中</span>
                    <span v-if="task.status=='done'" style="color:green">已完成</span>
                    <span v-if="task.status=='fail'" style="color:red">执行失败</span>
                    <span v-if="task.status=='terminated'" style="color:red">中止执行</span>
                </td>
                <td align="center">{{task.start_time}}</td>
                <td align="center">{{task.end_time}}</td>
                <td align="center" class="clickable">
                  <span v-if="task.status=='pending'" class="clickable" style="color:green"  @click="run_next(task.id)" >插队</span>
                  <span class="clickable ml-20" style="color:red"  @click="delete_task(task.id)" >删除</span>
                </td>
            </tr>
        </table>
    </div>
    <div v-if="show_panel=='taskboard'">
        <div class="flex-row pb-10 filter">
            <div style="padding-top: 10px; font-weight:bold;">日期：</div>
            <button class="ml-10" v-for="date in get_date_list()" @click="taskboard_filter.date=date;load_task_boards();"
                    :class="taskboard_filter.date==date?'btn_active':''">{{date}}</button>
        </div>
        <div class="flex-row pb-10 filter" style="display:inline-block; width: auto">
            <div style="padding-top: 10px; font-weight:bold;">用户：</div>
            <div v-for="(account, index) in accounts"  style="display:inline-block; width: auto" >
                <button class="ml-10"  @click="taskboard_filter.user_id=index"
                    v-if="index.toString() in task_boards" style="background-color:lightgreen"
                    :class="taskboard_filter.user_id==index?'btn_active':''">{{index}}</button>
                <button class="ml-10"  @click="taskboard_filter.user_id=index"
                    v-else :class="taskboard_filter.user_id==index?'btn_active':''">{{index}}</button>
            </div>
        </div>
        <table style="border:1px; width: 80%">
            <thead>
            <td align="center" width="50px">用户</td>
            <td align="center" width="120px">开始时间</td>
            <td align="center" width="120px">结束时间</td>
            <td align="center" width="200px">任务数</td>
            <td align="center" width="100px">总花费</td>
            <td align="center" width="100px">收益 (Coin/P)</td>
            </thead>
            <tr v-for="(tb, index) in task_boards" :key="index" v-show="taskboard_filter.user_id==0 || index==taskboard_filter.user_id">
                <td align="center">
                  <span v-if="tb.is_vip" style="color:orange; size: 12px">{{tb.user_id}}</span>
                      <span v-else>{{tb.user_id}}</span>
                </td>
                <td align="center"><span>{{tb.min_time}}</span></td>
                <td align="center"><span>{{tb.max_time}}</span></td>
                <td class="pl-5" align="center">
                    {{tb.total_deliver}}
                    <a :href="'taskboard.html?user_id=' + index" class="pl-20" target="_blank">未交任务</a>
                    <a :href="'taskboard.html?tab=deliver&user_id=' + index" class="pl-20" target="_blank">已交任务</a>
                </td>
                <td align="center">{{format_number(tb.total_cost)}}</td>
                <td align="center">
                    <span v-if="tb.total_p">
                        {{tb.total_p}}  ({{tb.p_price}})
                    </span>
                    <span v-else> - </span>
                </td>
            </tr>
        </table>
    </div>
    <div v-if="show_panel=='snapshot'">
      <div class="flex-row">
            <div class="flex-col pl-20 pixel_total">
                <div class="justify-center">
                    <img src="https://d31ss916pli4td.cloudfront.net/uploadedAssets/currency/pixels_pixel_currency_new_2.png">
                </div>
                <div class="justify-center">{{p_total.toFixed(1)}} (+{{(p_total - prev_p_total).toFixed(1)}})</div>
            </div>
            <div class="flex-col pl-20 coin_total">
                <div class="justify-center">
                    <img src="https://d31ss916pli4td.cloudfront.net/uploadedAssets/currency/cur_coins/e66e4f48-2b99-45f8-b8fc-74d43e391d1c.png">
                </div>
                <div class="justify-center">{{format_number(coin_total)}}({{format_number(coin_total - prev_coin_total)}})
                </div>
            </div>
            <div class="flex-row pl-20 pb-10 filter">
                <div style="padding-top: 10px; font-weight:bold;">排序：</div>
                <button @click="apply_sort('active')" :class="sort=='active'?'btn_active':''">活跃时间↑</button>
                <button @click="apply_sort('energy')" :class="sort=='energy'?'btn_active':''" class="ml-10">剩余能量↓
                </button>
                <button @click="apply_sort('p')" :class="sort=='p'?'btn_active':''" class="ml-10">PIXEL↓</button>
                <button @click="apply_sort('p_new')" :class="sort=='p_new'?'btn_active':''" class="ml-10">PIXEL_NEW↓
                </button>
                <button @click="apply_sort('coin')" :class="sort=='coin'?'btn_active':''" class="ml-10">金币↓</button>
                <button @click="apply_sort('skill')" :class="sort=='skill'?'btn_active':''" class="ml-10">技能等级↓
                </button>
                <input class="ml-10" style="width:200px" placeholder=" 按物品名称模糊搜索" v-model="keyword"
                       @keyup.enter="apply_search"/>
            </div>
        </div>
        <div class="flex-row user_row" v-for="([key, data],index) in data_list" :key="index" v-if="has_account(key)">
            <div class="flex-row justify-between">
                <div class="name pl-10">
                    {{key}}<span v-if="is_vip(key)">(VIP)</span>
                </div>
                <div class="flex-col pl-20 skill">
                    <div class="justify-center">
                        <img src="https://d31ss916pli4td.cloudfront.net/game/ui/hud/hud_temp_icon_skills.png">
                    </div>
                    <div class="justify-center">{{format_number(data.skill_sum)}}</div>
                    <div class="skill-hover">
                        <div v-for="(level, skill_name) in data.skills" :key="skill_name">{{skill_name}} - {{level}}
                        </div>
                    </div>
                </div>
                <div class="flex-col pl-20 pixel">
                    <div class="justify-center">
                        <img src="https://d31ss916pli4td.cloudfront.net/uploadedAssets/currency/pixels_pixel_currency_new_2.png">
                    </div>
                    <div class="justify-center">{{data.p ? data.p.toFixed(1) : '-'}}<span
                            v-if="data.new_p > 0"> (+{{data.new_p.toFixed(1)}})</span></div>
                </div>
                <div class="flex-col pl-20 energy">
                    <div class="justify-center">
                        <img src="https://d31ss916pli4td.cloudfront.net/game/ui/hud/hud_icon_energy.png">
                    </div>
                    <div class="justify-center">{{data.energy || '-'}}</div>
                </div>
                <div class="flex-col pl-20 coin">
                    <div class="justify-center">
                        <img src="https://d31ss916pli4td.cloudfront.net/uploadedAssets/currency/cur_coins/e66e4f48-2b99-45f8-b8fc-74d43e391d1c.png">
                    </div>
                    <div class="justify-center">{{format_number(data.coin)}} ({{data.coin - (data.prev || {}).coin || 0}})
                    </div>
                </div>
                <div class="flex-col pl-20" style="width:160px">
                    <div v-if="key in user_task_summaries">
                        <div v-if="user_task_summaries[key].running" class="justify-left" style="background-color:green; padding: 10px">
                            成功/失败: <span style="color:blue">{{user_task_summaries[key].done || 0}}</span>
                            / <span style="color:red">{{(user_task_summaries[key].terminated || 0) + (user_task_summaries[key].fail || 0)}}</span>
                        </div>
                        <div v-else class="justify-left" style="background-color:gray; padding: 10px">
                            成功/失败: <span style="color:blue">{{user_task_summaries[key].done || 0}}</span>
                            / <span style="color:red">{{(user_task_summaries[key].terminated || 0) + (user_task_summaries[key].fail || 0)}}</span>
                        </div>
                    </div>
                </div>
                <div class="flex-col pl-10 update_time">
                    <div class="justify-center">最后更新时间</div>
                    <div class="justify-center" style="color: red;" v-if="time_diff(data.last_update) > 60 * 60 * 12">
                        {{data.last_update}}
                    </div>
                    <div class="justify-center" v-else>
                        {{data.last_update}}
                    </div>
                </div>
                <div class="flex-row backpack">
                    <div class="flex-col backpack_item" v-for="index in get_backpack_count(key)"
                         :class="get_cell_class(index, data.backpack)">
                        <div v-if="get_backpack_qty(index, data.backpack)>0" class="backpack_item_container">
                            <div class="justify-center backpack_item_name">
                                {{get_backpack_name(index, data.backpack)}}
                            </div>
                            <div class="justify-center backpack_item_qty">
                                {{get_backpack_qty(index, data.backpack)}}
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>